package jiang.jredis.channel;

import jiang.jredis.channel.annotation.Topic;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.NonNull;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 执行包扫描逻辑的BeanDefinitionRegistryPostProcessor
 * 扫描所有加了@Topic注解的接口，并进行代理
 * @see Topic
 */
public class ChannelScanBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, ApplicationContextAware {

    /**
     * 设置优先级
     * 必须在依赖的Bean创建之后才能扫描Channel进行代理创建
     */
    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;  // within PriorityOrdered
    }

    /**
     * Aware属性注入
     */
    private ApplicationContext applicationContext;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**
     * 添加bean定义信息的后置处理方法
     * @param beanDefinitionRegistry the bean definition registry used by the application context
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {

        // 获取扫描路径
        List<String> basePackageList = new ArrayList<>();
        Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(ComponentScan.class);
        for (Object bean : beansWithAnnotation.values()) {
            ComponentScan componentScan = bean.getClass().getAnnotation(ComponentScan.class);

            // 如果没有指定路径，默认使用注解类的当前包路径
            if(componentScan == null || componentScan.value().length == 0) {
                String value = bean.getClass().getPackage().getName();
                basePackageList.add(value);
                continue;
            }

            // 如果指定了路径就直接加入列表
            for(String value : componentScan.value()) {
                basePackageList.add(value);
            }
        }
        String[] basePackages = basePackageList.toArray(new String[0]);

        // 创建包扫描器并开始扫描 并且不使用默认过滤器
        ClassPathBeanDefinitionScanner classPathBeanDefinitionScanner = new ClassPathBeanDefinitionScanner(beanDefinitionRegistry, false) {

            /**
             * 设置扫描接口
             */
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
            }

            /**
             * 扫描方法
             */
            @Override
            protected Set<BeanDefinitionHolder> doScan(@NonNull String... basePackages) {
                // 扫描
                Set<BeanDefinitionHolder> beanDefinitionHolders = super.doScan(basePackages);

                // 遍历扫描出的Channel接口 创建代理类加入Spring容器
                for (BeanDefinitionHolder definitionHolder : beanDefinitionHolders) {

                    // 获取bean信息
                    AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) definitionHolder.getBeanDefinition();
                    String beanClassName = beanDefinition.getBeanClassName();

                    // 设置工厂bean 用于创建channel对象
                    beanDefinition.setBeanClass(ChannelFactoryBean.class);

                    // 传入工厂bean的构造参数
                    ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues();
                    constructorArgumentValues.addGenericArgumentValue(beanClassName);
                }
                return beanDefinitionHolders;
            }
        };

        // 过滤出@Topic注解
        classPathBeanDefinitionScanner.addIncludeFilter(new AnnotationTypeFilter(Topic.class));

        // 开始扫描
        classPathBeanDefinitionScanner.scan(basePackages);
    }

    /**
     * 不需要该方法执行业务逻辑
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }
}
